/*
 * AUCFitness.java
 *
 * Created on den 15 augusti 2006, 00:19
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package grex.fitnessfunctions.Classification;
import grex.Environment;
import grex.GP;
import grex.Options;
import grex.Prediction;
import grex.PredictionContainer;
import grex.fitnessfunctions.FitnessFunction;
import grex.fitnessfunctions.IFitnessFunction;
import grex.genes.GeneException;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class AUCFitness extends FitnessFunction implements IFitnessFunction, Serializable{
  //  double P=0,N=0,positive = 1;
   // ArrayList<ProbValuePair> posProbvValues;
    /** Creates a new instance of AUCFitness */
    public AUCFitness() {
        super(FitnessFunction.AUC);
    }
    

    @Override
    protected double calcFinalError(PredictionContainer pc){
     //   if(environment == null)
      //      environment = gp.getEnvironment();
        double auc = 0;
     //   ArrayList<Prediction> preds = gp.getOrderedTrain();
        for (int positive = 0; positive < getNrOfTargetCategories(); positive++) {
            List<ProbValuePair> posProbvValues = Collections.synchronizedList(new  ArrayList<ProbValuePair>());
            
            double P = 0, N = 0;
            double[] probs;
            for (Prediction p : pc.values()) {
               // p.getPrediction();
                probs= p.getProbs();
                if(probs!=null)
                    posProbvValues.add(new ProbValuePair(probs[positive], p.getTargetValue()));
                else //Dodgy special case which only effects the TreePanel and not the actual result reported in the summary tab or the resultfile. Happen once in 100.000 fitness caclulation due to thread complications 
                    posProbvValues.add(new ProbValuePair(0, p.getTargetValue()));
                if (p.getTargetValue() == positive) {
                    P++;
                } else {
                    N++;
                }
            }

            Collections.sort(posProbvValues);
            /*
            try{
            Collections.sort(posProbvValues);
            }catch(Exception e){
                System.out.println("fel i soretering");
                for(ProbValuePair p:posProbvValues)
                    System.out.println("p:"+ p.prob + " v:"+p.value);
                e.printStackTrace();
            }//*/

            double FP = 0, FPprev = 0, TP = 0, TPprev = 0, A = 0, fi = 0, fprev = Double.MIN_VALUE, realValue = 0;

            for (ProbValuePair pv : posProbvValues) {
                fi = pv.getProb();
                realValue = pv.getValue();
                if (fi != fprev) {
                    A = A + trap_area(FPprev, FP, TPprev, TP);
                    fprev = fi;
                    FPprev = FP;
                    TPprev = TP;
                }
                if (realValue == positive) {
                    TP = TP + 1;
                } else {
                    FP = FP + 1;
                }
            }
            A = A + trap_area(FPprev, FP, TPprev, TP);
            A = A + trap_area(FP, N, TP, P);
            double frequency = 0;
            if(P*N!=0){
                 A = A / (P * N);
                 frequency = P / getNrOfInstances();
            }else
                A=0;
            
            auc += A * frequency;
        }
        
        double trainFitness = 100 * (1-auc);
        return trainFitness;
    }



    @Override
    protected double calcPredictionError(Prediction prediction, double targetValue) {
        return 0;
    }
    
    @Override
    protected double normalizeTotalError(double totalError){
        return totalError;
    }
    
    private double trap_area(double x1, double x2,double y1,double y2){
        double base = Math.abs(x1-x2);
        double heightAvg = (y1+y2)/2;
        return base * heightAvg;
    }    
    
     class ProbValuePair implements Comparable, Serializable{
        double prob,value;
        
        public ProbValuePair(double prob,double value){
            this.prob=prob;
            this.value=value;
        }
        
        public int compareTo(Object o) {
            ProbValuePair pv = (ProbValuePair)o;
            if(prob > pv.getProb())
                return -1;
            if(prob < pv.getProb())
                return 1;
            return 0;            
        }
        
        public double getProb(){
            return prob;
        }
        
        public double getValue(){
            return value;
        }
                

        public String toString() {
            return "prob: " + prob+ " value: " + value;
        }                
    }
    
}